#include <unistd.h>
#include <setjmp.h>
#include <signal.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>

#include <sys/types.h>
#include <sys/socket.h>

#include <time.h>
#include "bandwidthd.h"

#ifdef HAVE_ARPA_NAMESER_H
#include <arpa/nameser.h>
#endif
#ifdef HAVE_RESOLV_H
#include <resolv.h>
#endif

extern unsigned int SubnetCount;
extern struct config config;

jmp_buf dnsjump;

static void rdnslngjmp(int signal);

void rdns(char *Buffer, unsigned long IP)  // This takes over sigalarm!
	{
#ifdef HAVE_RESOLV_H
	char DNSError[] = "DNS Timeout: Correct to speed up graphing";
	char None[] = "Configure DNS to reverse this IP";
	char TooManyDNSTimeouts[] = "Too many dns timeouts, reverse lookups suspended";
	struct hostent *hostent;
	char chrIP[50];
	static int Init = TRUE;
	static int DNSTimeouts = 0;  // This is reset for each run because we're forked
	unsigned long addr = htonl(IP);

    _res.retrans = 1;
    _res.retry = 2;

	if (Init)
		{
        signal(SIGALRM, rdnslngjmp);
		Init = FALSE;
		}

	if (DNSTimeouts > 100)
		{
		syslog(LOG_ERR, "Too many dns timeouts, reverse lookups suspended");
        strncpy(Buffer, TooManyDNSTimeouts, 253);
		Buffer[254] = '\0';
		return;
		}		

	if (setjmp(dnsjump) == 0)
		{
		alarm(10);  // Don't let gethostbyaddr hold us up too long
		hostent = gethostbyaddr((char *) &addr, 4, AF_INET); // (char *)&Data->IP				
		alarm(0);
		
		if (hostent)
			sprintf(Buffer, "%s", hostent->h_name);
		else
			{
	        strncpy(Buffer, None, 253);
			Buffer[254] = '\0';
			}
		}
	else  // Our alarm timed out
		{
		HostIp2CharIp(IP, chrIP);
		syslog(LOG_ERR, "DNS timeout for %s: This problem reduces graphing performance", chrIP);
		DNSTimeouts++;
        strncpy(Buffer, DNSError, 253);
		Buffer[254] = '\0';
		}
#else
	Buffer[0] = '\0';
#endif
	}

static void rdnslngjmp(int signal)
	{
    longjmp(dnsjump, 1);
	}

void swap(struct SummaryData **a, struct SummaryData **b) {
	struct SummaryData *temp;
    temp = *a; *a = *b; *b = temp;
}
void QuickSortSummaryData(struct SummaryData *SummaryData[], int left, int right) {
    int i,j,center;
    unsigned long long pivot;
    if (left==right) return;
    if (left+1==right) {
        if (SummaryData[left]->Total < SummaryData[right]->Total)
            swap(&SummaryData[left],&SummaryData[right]);
        return;
    }
    /* use the median-of-three method for picking pivot */
    center = (left+right)/2;
    if (SummaryData[left]->Total < SummaryData[center]->Total)
        swap(&SummaryData[left],&SummaryData[center]);
    if (SummaryData[left]->Total < SummaryData[right]->Total)
        swap(&SummaryData[left],&SummaryData[right]);
    if (SummaryData[center]->Total < SummaryData[right]->Total)
        swap(&SummaryData[center],&SummaryData[right]);
    pivot = SummaryData[center]->Total;
    swap(&SummaryData[center],&SummaryData[right-1]); /* hide the pivot */
    i = left; j = right - 1;
    do {
        do { ++i; } while (SummaryData[i]->Total > pivot);
        do { --j; } while (SummaryData[j]->Total < pivot);
        swap(&SummaryData[i],&SummaryData[j]);
    } while (j > i);
    swap(&SummaryData[i],&SummaryData[j]); /* undo last swap */
    swap(&SummaryData[i],&SummaryData[right-1]); /* restore pivot */
    QuickSortSummaryData(SummaryData,left,i-1);
    QuickSortSummaryData(SummaryData,i+1,right);
}

#define NumFactor 1024
static void FormatNum(unsigned long long n, char *buf, int len) {
    double f;
    if (n<NumFactor) { snprintf(buf,len,"<td align=\"right\"><tt>%i&nbsp;</tt></td>",(int)n); return; }
    f = n;
    f /= NumFactor; if (f<NumFactor) { snprintf(buf,len,"<td align=\"right\"><tt>%.1fK</tt></td>",f); return; }
    f /= NumFactor; if (f<NumFactor) { snprintf(buf,len,"<td align=\"right\"><tt>%.1fM</tt></td>",f); return; }
    f /= NumFactor; if (f<NumFactor) { snprintf(buf,len,"<td align=\"right\"><tt>%.1fG</tt></td>",f); return; }
    f /= NumFactor; snprintf(buf,len,"<td align=\"right\"><tt>%.1fT</tt></td>\n",f);
}

void PrintTableLine(FILE *stream, struct SummaryData *Data, int Counter)
	{
	char Buffer1[50];
	char Buffer2[50];
	char Buffer3[50];
	char Buffer4[50];
	char Buffer4b[50];
	char Buffer5[50];
	char Buffer5b[50];
	char Buffer6[50];
	char Buffer7[50];
	char Buffer8[50];

	// First convert the info to nice, human readable stuff
	if (Data->IP == 0)
		strcpy(Buffer1, "Total");
	else
		HostIp2CharIp(Data->IP, Buffer1);

    FormatNum(Data->Total,         Buffer2,  50);
	FormatNum(Data->TotalSent,     Buffer3,  50);
	FormatNum(Data->TotalReceived, Buffer4,  50);
	FormatNum(Data->FTP, 		   Buffer4b, 50);
	FormatNum(Data->HTTP,          Buffer5,  50);
	FormatNum(Data->P2P,           Buffer5b, 50);
	FormatNum(Data->TCP,           Buffer6,  50);
	FormatNum(Data->UDP,           Buffer7,  50);
	FormatNum(Data->ICMP,          Buffer8,  50);

	if (Counter%4 == 0 || (Counter-1)%4 == 0)
		fprintf(stream, "<TR>");
	else
		fprintf(stream, "<TR bgcolor=lightblue>");

	if (Data->Graph)
		fprintf(stream, "<TD><a href=\"#%s-%c\">%s</a></TD>%s%s%s%s%s%s%s%s%s</TR>\n",
			Buffer1, // Ip
			config.tag,
			Buffer1, // Ip
			Buffer2, // Total
			Buffer3, // TotalSent
			Buffer4, // TotalReceived
			Buffer4b, // FTP
			Buffer5, // HTTP
			Buffer5b, // P2P
			Buffer6, // TCP
			Buffer7, // UDP
			Buffer8); // ICMP
	else
		fprintf(stream, "<TD>%s</TD>%s%s%s%s%s%s%s%s%s</TR>\n",
			Buffer1, // Ip
			Buffer2, // Total
			Buffer3, // TotalSent
			Buffer4, // TotalReceived
			Buffer4b, // FTP
			Buffer5, // HTTP
			Buffer5b, // P2P		
			Buffer6, // TCP
			Buffer7, // UDP
			Buffer8); // ICMP
	}

void MakeIndexPages(int NumIps, struct SummaryData *SummaryData[])
	{
	int SubnetCounter;
	int Counter, tCounter;
	time_t WriteTime;
	char filename[] = "./htdocs/index2.html";
	char *PeriodDesc;
	
	FILE *file;

	char Buffer1[50];
	char Buffer2[50];
	char HostName[255];

	WriteTime = time(NULL);
	
	QuickSortSummaryData(SummaryData, 0, NumIps-1);

	////////////////////////////////////////////////
	// Print main index page
	
	if (config.tag == '1')
		{
		if ((file = fopen("./htdocs/index.html", "wt")) == NULL)
			{
			syslog(LOG_ERR, "Failed to open ./htdocs/index.html");
			exit(1);
			}
		}
	else
		{
		filename[14] = config.tag;
		if ((file = fopen(filename, "wt")) == NULL)
			{
			syslog(LOG_ERR, "Failed to open %s", filename);
			exit(1);
			}
		}

	switch (config.tag)
		{
		case '1': PeriodDesc = "Daily"; break;
		case '2': PeriodDesc = "Weekly"; break;
		case '3': PeriodDesc = "Monthly"; break;
		case '4': PeriodDesc = "Yearly"; break;
		default: PeriodDesc = ""; break;
		}
	
	fprintf(file, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n");
	fprintf(file, "<HTML>\n<HEAD>\n<TITLE>Bandwidthd</TITLE>\n");

	if (config.meta_refresh)
		fprintf(file, "<META HTTP-EQUIV=\"REFRESH\" content=\"%u\">\n",
				config.meta_refresh);
	fprintf(file, "<META HTTP-EQUIV=\"EXPIRES\" content=\"-1\">\n");
	fprintf(file, "<META HTTP-EQUIV=\"PRAGMA\" content=\"no-cache\">\n");
	fprintf(file, "</HEAD>\n<BODY vlink=blue>\n%s<br>\n<center><img src=\"logo.gif\" ALT=\"Logo\"><BR>\n", ctime(&WriteTime));
	fprintf(file, "Programmed by David Hinkle, Commissioned by <a href=\"http://www.derbytech.com\">DerbyTech</a> wireless networking.<BR>");
	fprintf(file, "<BR>\n - <a href=\"index.html\">Daily</a> -- <a href=\"index2.html\">Weekly</a> -- ");
	fprintf(file, "<a href=\"index3.html\">Monthly</a> -- <a href=\"index4.html\">Yearly</a> - <BR>\n");

	fprintf(file, "<BR>\nPick a Subnet:<BR>\n");	
	if (config.tag == '1')
		fprintf(file, "- <a href=\"index.html\">Top20</a> -");
	else
		fprintf(file, "- <a href=\"index%c.html\">Top20</a> -", config.tag);

	for (Counter = 0; Counter < SubnetCount; Counter++)            
		{
		HostIp2CharIp(SubnetTable[Counter].ip, Buffer1);
		fprintf(file, "- <a href=\"Subnet-%c-%s.html\">%s</a> -", config.tag, Buffer1, Buffer1);
		}

	/////  TOP 20

	fprintf(file, "<H1>Top 20 IPs by Traffic - %s</H1></center>", PeriodDesc);
	fprintf(file, "<center>\n<table width=\"100%%\" border=1 cellspacing=0>\n");

    // PASS 1:  Write out the table

	fprintf(file, "<TR bgcolor=lightblue><TD>Ip and Name<TD align=center>Total<TD align=center>Total Sent<TD align=center>Total Received<TD align=center>FTP<TD align=center>HTTP<TD align=center>P2P<TD align=center>TCP<TD align=center>UDP<TD align=center>ICMP\n");
	for (Counter=0; Counter < 21 && Counter < NumIps; Counter++)
		PrintTableLine(file, SummaryData[Counter], Counter);

	fprintf(file, "</table></center>\n");

	// PASS 2: The graphs
	for (Counter=0; Counter < 21 && Counter < NumIps; Counter++)
		if (SummaryData[Counter]->Graph)
			{
			if (SummaryData[Counter]->IP == 0)
				{
				strcpy(Buffer1, "Total");	
				strcpy(HostName, "Total of all subnets");
				}
			else
				{	
				HostIp2CharIp(SummaryData[Counter]->IP, Buffer1);
				rdns(HostName, SummaryData[Counter]->IP);
				}
			fprintf(file, "<a name=\"%s-%c\"></a><H1><a href=\"#top\">(Top)</a> %s - %s</H1><BR>\nSend:<br>\n<img src=%s-%c-S.png ALT=\"Sent traffic for %s\"><BR>\n<img src=legend.gif ALT=\"Legend\"><br>\nReceived:<br>\n<img src=%s-%c-R.png ALT=\"Sent traffic for %s\"><BR>\n<img src=legend.gif ALT=\"Legend\"><br>\n<BR>\n", Buffer1, config.tag, Buffer1, HostName, Buffer1, config.tag, Buffer1, Buffer1, config.tag, Buffer1);
			}

	fprintf(file, "</BODY></HTML>\n");

	fclose(file);

	////////////////////////////////////////////////
	// Print each subnet page

	for (SubnetCounter = 0; SubnetCounter < SubnetCount; SubnetCounter++)
		{
		HostIp2CharIp(SubnetTable[SubnetCounter].ip, Buffer1);
		sprintf(Buffer2, "./htdocs/Subnet-%c-%s.html", config.tag, Buffer1);
		file = fopen(Buffer2, "wt");
		fprintf(file, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n");
		fprintf(file, "<HTML>\n<HEAD><TITLE>Bandwidthd - Subnet %s</TITLE>\n", Buffer1);

		if (config.meta_refresh)
			fprintf(file, "<META HTTP-EQUIV=\"REFRESH\" content=\"%u\">\n",
					config.meta_refresh);
		fprintf(file, "<META HTTP-EQUIV=\"EXPIRES\" content=\"-1\">\n");
		fprintf(file, "<META HTTP-EQUIV=\"PRAGMA\" content=\"no-cache\">\n");
		fprintf(file, "</HEAD>\n<BODY vlink=blue>\n%s<br>\n<CENTER><a name=\"Top\"></a>", ctime(&WriteTime));
		fprintf(file, "<img src=\"logo.gif\" ALT=\"Logo\"><BR>");
		fprintf(file, "Programmed by David Hinkle, Commissioned by <a href=\"http://www.derbytech.com\">DerbyTech</a> wireless networking.<BR>\n");

		fprintf(file, "<BR>\n - <a href=\"index.html\">Daily</a> -- <a href=\"index2.html\">Weekly</a> -- ");
		fprintf(file, "<a href=\"index3.html\">Monthly</a> -- <a href=\"index4.html\">Yearly</a> - <BR>\n");

		fprintf(file, "<BR>\nPick a Subnet:<BR>\n");
		if (config.tag == '1')
			fprintf(file, "- <a href=\"index.html\">Top20</a> -");
		else
			fprintf(file, "- <a href=\"index%c.html\">Top20</a> -", config.tag);

		for (Counter = 0; Counter < SubnetCount; Counter++)
			{
			HostIp2CharIp(SubnetTable[Counter].ip, Buffer2);
			fprintf(file, "- <a href=\"Subnet-%c-%s.html\">%s</a> -", config.tag, Buffer2, Buffer2);
			}

		fprintf(file, "<H1>%s - %s</H1></center>", Buffer1, PeriodDesc);
		fprintf(file, "<table width=\"100%%\" border=1 cellspacing=0>\n");

        // PASS 1:  Write out the table

		fprintf(file, "<TR bgcolor=lightblue><TD>Ip and Name<TD align=center>Total<TD align=center>Total Sent<TD align=center>Total Received<TD align=center>FTP<TD align=center>HTTP<TD align=center>P2P<TD align=center>TCP<TD align=center>UDP<TD align=center>ICMP\n");
		for (tCounter=0, Counter=0; Counter < NumIps; Counter++)
			{
            if (SubnetTable[SubnetCounter].ip == (SummaryData[Counter]->IP & SubnetTable[SubnetCounter].mask))
				{ // The ip belongs to this subnet
				PrintTableLine(file, SummaryData[Counter], tCounter++);
    			}
			}

		fprintf(file, "</table>\n");

		// PASS 2: The graphs
		for (Counter=0; Counter < NumIps; Counter++)
			{
            if (SubnetTable[SubnetCounter].ip == (SummaryData[Counter]->IP & SubnetTable[SubnetCounter].mask))
				{ // The ip belongs to this subnet
				if (SummaryData[Counter]->Graph)
					{
					HostIp2CharIp(SummaryData[Counter]->IP, Buffer1);
					rdns(HostName, SummaryData[Counter]->IP);
					fprintf(file, "<a name=\"%s-%c\"></a><H1><a href=\"#top\">(Top)</a> %s - %s</H1><BR>\nSend:<br>\n<img src=%s-%c-S.png ALT=\"Sent traffic graph for %s\"><BR>\n<img src=legend.gif ALT=\"Legend\"><br>\nReceived:<br>\n<img src=%s-%c-R.png ALT=\"Received traffic for %s\"><BR>\n<img src=legend.gif ALT=\"Legend\"><br>\n<BR>\n", Buffer1, config.tag, Buffer1, HostName, Buffer1, config.tag, Buffer1, Buffer1, config.tag, Buffer1);
					}
				}
			}

		fprintf(file, "</BODY></HTML>\n");
		fclose(file);
		}

	free(SummaryData);
	}

void GraphIp(struct IPDataStore *DataStore, struct SummaryData *SummaryData, time_t timestamp)
    {
    FILE *OutputFile;
    char outputfilename[50];
    gdImagePtr im, im2;
    int white;
    unsigned long long int YMax;
	char CharIp[20];

    time_t GraphBeginTime;

	// TODO: First determine if graph will be printed before creating image and drawing backround, etc

	if (DataStore->ip == 0)
		strcpy(CharIp, "Total");
	else
		HostIp2CharIp(DataStore->ip, CharIp);

    GraphBeginTime = timestamp - config.range;

    im = gdImageCreate(XWIDTH, YHEIGHT);
    white = gdImageColorAllocate(im, 255, 255, 255);
    //gdImageFill(im, 10, 10, white);

    im2 = gdImageCreate(XWIDTH, YHEIGHT);
    white = gdImageColorAllocate(im2, 255, 255, 255);
    //gdImageFill(im2, 10, 10, white);

    YMax = GraphData(im, im2, DataStore, GraphBeginTime, SummaryData);
    if (YMax != 0)
        {
        // Finish the graph
        PrepareXAxis(im, timestamp);
        PrepareYAxis(im, YMax);

        PrepareXAxis(im2, timestamp);
        PrepareYAxis(im2, YMax);

        sprintf(outputfilename, "./htdocs/%s-%c-S.png", CharIp, config.tag);
        OutputFile = fopen(outputfilename, "wb");    
        gdImagePng(im, OutputFile);
        fclose(OutputFile);

        sprintf(outputfilename, "./htdocs/%s-%c-R.png", CharIp, config.tag);
        OutputFile = fopen(outputfilename, "wb");
        gdImagePng(im2, OutputFile);
        fclose(OutputFile);
        }
    else
        {
        // The graph isn't worth clutering up the web pages with
        sprintf(outputfilename, "./htdocs/%s-%c-R.png", CharIp, config.tag);
        unlink(outputfilename);
        sprintf(outputfilename, "./htdocs/%s-%c-S.png", CharIp, config.tag);
        unlink(outputfilename);
        }

	gdImageDestroy(im);
	gdImageDestroy(im2);
    }

// Returns YMax
unsigned long long int GraphData(gdImagePtr im, gdImagePtr im2, struct IPDataStore *DataStore, time_t timestamp, struct SummaryData *SummaryData)
    {
    unsigned long long int YMax=0;
	
	struct DataStoreBlock *CurrentBlock;
    struct IPData *Data;

	// TODO: These should be a structure!!!!
	// TODO: This is an awfull lot of data to be allocated on the stack

    unsigned long long total[XWIDTH];
    unsigned long long icmp[XWIDTH];
    unsigned long long udp[XWIDTH];
    unsigned long long tcp[XWIDTH];
	unsigned long long ftp[XWIDTH];
    unsigned long long http[XWIDTH];
    unsigned long long p2p[XWIDTH];
    int Count[XWIDTH];

    unsigned long long total2[XWIDTH];
    unsigned long long icmp2[XWIDTH];
    unsigned long long udp2[XWIDTH];
    unsigned long long tcp2[XWIDTH];
	unsigned long long ftp2[XWIDTH];
    unsigned long long http2[XWIDTH];
    unsigned long long p2p2[XWIDTH];

    size_t DataPoints;
    double x;
    int xint;
    int Counter;
    char Buffer[30];
    char Buffer2[50];
    
    int blue, lblue, red, purple, green, brown, black;
    int blue2, lblue2, red2, purple2, green2, brown2, black2;

	unsigned long long int SentPeak = 0;
	unsigned long long int ReceivedPeak = 0;

    purple   = gdImageColorAllocate(im, 255, 0, 255);
    green    = gdImageColorAllocate(im, 0, 255, 0);
    blue     = gdImageColorAllocate(im, 0, 0, 255);
	lblue	 = gdImageColorAllocate(im, 128, 128, 255);
    brown    = gdImageColorAllocate(im, 128, 0, 0);
    red      = gdImageColorAllocate(im, 255, 0, 0);
    black 	 = gdImageColorAllocate(im, 0, 0, 0);
    
    purple2   = gdImageColorAllocate(im2, 255, 0, 255);
    green2   = gdImageColorAllocate(im2, 0, 255, 0);
    blue2    = gdImageColorAllocate(im2, 0, 0, 255);
	lblue2	 = gdImageColorAllocate(im2, 128, 128, 255);
    brown2   = gdImageColorAllocate(im2, 128, 0, 0);
    red2     = gdImageColorAllocate(im2, 255, 0, 0);
    black2   = gdImageColorAllocate(im2, 0, 0, 0);

	CurrentBlock = DataStore->FirstBlock;
	Data = CurrentBlock->Data;
    DataPoints = CurrentBlock->NumEntries;

	memset(SummaryData, 0, sizeof(struct SummaryData));
	SummaryData->IP = Data[0].ip;
	
    memset(Count, 0, sizeof(Count[0])*XWIDTH);

    memset(total, 0, sizeof(total[0])*XWIDTH);
    memset(icmp, 0, sizeof(total[0])*XWIDTH);
    memset(udp, 0, sizeof(total[0])*XWIDTH);
    memset(tcp, 0, sizeof(total[0])*XWIDTH);
	memset(ftp, 0, sizeof(total[0])*XWIDTH);
    memset(http, 0, sizeof(total[0])*XWIDTH);
    memset(p2p, 0, sizeof(total[0])*XWIDTH);

    memset(total2, 0, sizeof(total[0])*XWIDTH);
    memset(icmp2, 0, sizeof(total[0])*XWIDTH);
    memset(udp2, 0, sizeof(total[0])*XWIDTH);
    memset(tcp2, 0, sizeof(total[0])*XWIDTH);
    memset(ftp2, 0, sizeof(total[0])*XWIDTH);
    memset(http2, 0, sizeof(total[0])*XWIDTH);
    memset(p2p2, 0, sizeof(total[0])*XWIDTH);

	// Change this to just run through all the datapoints we have stored in ram

	// Sum up the bytes/second
    while(DataPoints > 0)  // We have data to graph
        {
        for (Counter = 0; Counter < DataPoints; Counter++)  // Graph it all
            {
            x = (Data[Counter].timestamp-timestamp)*((XWIDTH-XOFFSET)/config.range)+XOFFSET;        
            xint = x;

            if (xint >= 0 && xint < XWIDTH)
                {
                Count[xint]++;
				
				if (Data[Counter].Send.total > SentPeak)
					SentPeak = Data[Counter].Send.total;
       	        total[xint] += Data[Counter].Send.total;
           	    icmp[xint] += Data[Counter].Send.icmp;
               	udp[xint] += Data[Counter].Send.udp;
                tcp[xint] += Data[Counter].Send.tcp;
				ftp[xint] += Data[Counter].Send.ftp;
       	        http[xint] += Data[Counter].Send.http;
				p2p[xint] += Data[Counter].Send.p2p;

                if (Data[Counter].Receive.total > ReceivedPeak)
   	            	ReceivedPeak = Data[Counter].Receive.total;
       	        total2[xint] += Data[Counter].Receive.total;
           	    icmp2[xint] += Data[Counter].Receive.icmp;
               	udp2[xint] += Data[Counter].Receive.udp;
                tcp2[xint] += Data[Counter].Receive.tcp;
				ftp2[xint] += Data[Counter].Receive.ftp;
       	        http2[xint] += Data[Counter].Receive.http;
				p2p2[xint] += Data[Counter].Receive.p2p;
                }
            }

		CurrentBlock = CurrentBlock->Next;
			
		if (CurrentBlock)
			{
         	Data = CurrentBlock->Data;
			DataPoints = CurrentBlock->NumEntries;
			}
		else
			DataPoints = 0;		
        }

	// Convert SentPeak and ReceivedPeak from bytes to bytes/second
	SentPeak /= config.interval; ReceivedPeak /= config.interval;

    // Preform the Average
    for(Counter=XOFFSET+1; Counter < XWIDTH; Counter++)
            {
            if (Count[Counter] > 0)
                {
            	SummaryData->Total += total[Counter] + total2[Counter];
				SummaryData->TotalSent += total[Counter];
 				SummaryData->TotalReceived += total2[Counter];
				SummaryData->TCP += tcp[Counter] + tcp2[Counter];
				SummaryData->FTP += ftp[Counter] + ftp2[Counter];
				SummaryData->HTTP += http[Counter] + http2[Counter];
				SummaryData->P2P += p2p[Counter] + p2p2[Counter];
				SummaryData->UDP += udp[Counter] + udp2[Counter];
				SummaryData->ICMP += icmp[Counter] + icmp2[Counter];

                // Preform the average
                total[Counter] /= (Count[Counter]*config.interval);
                tcp[Counter] /= (Count[Counter]*config.interval);
                ftp[Counter] /= (Count[Counter]*config.interval);
                http[Counter] /= (Count[Counter]*config.interval);
				p2p[Counter] /= (Count[Counter]*config.interval);
                udp[Counter] /= (Count[Counter]*config.interval);
                icmp[Counter] /= (Count[Counter]*config.interval);
								
                total2[Counter] /= (Count[Counter]*config.interval);
                tcp2[Counter] /= (Count[Counter]*config.interval);
				ftp2[Counter] /= (Count[Counter]*config.interval);
                http2[Counter] /= (Count[Counter]*config.interval);
				p2p2[Counter] /= (Count[Counter]*config.interval);
                udp2[Counter] /= (Count[Counter]*config.interval);
                icmp2[Counter] /= (Count[Counter]*config.interval);


                if (total[Counter] > YMax)
                    YMax = total[Counter];
                
                if (total2[Counter] > YMax)
                    YMax = total2[Counter];
                }
            }

    YMax += YMax*0.05;    // Add an extra 5%
	
    if ((SummaryData->IP != 0 && SummaryData->Total < config.graph_cutoff))
		{
		SummaryData->Graph = FALSE;
        return(0);
		}
	else
        SummaryData->Graph = TRUE;

    // Plot the points
    for(Counter=XOFFSET+1; Counter < XWIDTH; Counter++)    
            {
            if (Count[Counter] > 0)
                {
                // Convert the bytes/sec to y coords
                total[Counter] = (total[Counter]*(YHEIGHT-YOFFSET))/YMax;
                tcp[Counter] = (tcp[Counter]*(YHEIGHT-YOFFSET))/YMax;
                ftp[Counter] = (ftp[Counter]*(YHEIGHT-YOFFSET))/YMax;
                http[Counter] = (http[Counter]*(YHEIGHT-YOFFSET))/YMax;
                p2p[Counter] = (p2p[Counter]*(YHEIGHT-YOFFSET))/YMax;
                udp[Counter] = (udp[Counter]*(YHEIGHT-YOFFSET))/YMax;
                icmp[Counter] = (icmp[Counter]*(YHEIGHT-YOFFSET))/YMax;

                total2[Counter] = (total2[Counter]*(YHEIGHT-YOFFSET))/YMax;
                tcp2[Counter] = (tcp2[Counter]*(YHEIGHT-YOFFSET))/YMax;
                ftp2[Counter] = (ftp2[Counter]*(YHEIGHT-YOFFSET))/YMax;
                http2[Counter] = (http2[Counter]*(YHEIGHT-YOFFSET))/YMax;
				p2p2[Counter] = (p2p2[Counter]*(YHEIGHT-YOFFSET))/YMax;
                udp2[Counter] = (udp2[Counter]*(YHEIGHT-YOFFSET))/YMax;
                icmp2[Counter] = (icmp2[Counter]*(YHEIGHT-YOFFSET))/YMax;

                // Stack 'em up!
                // Total is stacked from the bottom
                // Icmp is on the bottom too
                // Udp is stacked on top of icmp
                udp[Counter] += icmp[Counter];
				udp2[Counter] += icmp2[Counter];
                // TCP and p2p are stacked on top of Udp
                tcp[Counter] += udp[Counter];
                tcp2[Counter] += udp2[Counter];
                p2p[Counter] += udp[Counter];
                p2p2[Counter] += udp2[Counter];
				// Http is stacked on top of p2p
                http[Counter] += p2p[Counter];
                http2[Counter] += p2p2[Counter];
				// Ftp is stacked on top of http
                ftp[Counter] += http[Counter];
                ftp2[Counter] += http2[Counter];

                // Plot them!
				// Sent
                gdImageLine(im, Counter, (YHEIGHT-YOFFSET) - icmp[Counter], Counter, YHEIGHT-YOFFSET-1, red);
                gdImageLine(im, Counter, (YHEIGHT-YOFFSET) - udp[Counter], Counter, (YHEIGHT-YOFFSET) - icmp[Counter] - 1, brown);
                gdImageLine(im, Counter, (YHEIGHT-YOFFSET) - tcp[Counter], Counter, (YHEIGHT-YOFFSET) - udp[Counter] - 1, green);
                gdImageLine(im, Counter, (YHEIGHT-YOFFSET) - p2p[Counter], Counter, (YHEIGHT-YOFFSET) - udp[Counter] - 1, purple);
                gdImageLine(im, Counter, (YHEIGHT-YOFFSET) - http[Counter], Counter, (YHEIGHT-YOFFSET) - p2p[Counter] - 1, blue);
                gdImageLine(im, Counter, (YHEIGHT-YOFFSET) - ftp[Counter], Counter, (YHEIGHT-YOFFSET) - http[Counter] - 1, lblue);
								
				// Receive
                gdImageLine(im2, Counter, (YHEIGHT-YOFFSET) - icmp2[Counter], Counter, YHEIGHT-YOFFSET-1, red2);
                gdImageLine(im2, Counter, (YHEIGHT-YOFFSET) - udp2[Counter], Counter, (YHEIGHT-YOFFSET) - icmp2[Counter] - 1, brown2);
                gdImageLine(im2, Counter, (YHEIGHT-YOFFSET) - tcp2[Counter], Counter, (YHEIGHT-YOFFSET) - udp2[Counter] - 1, green2);
                gdImageLine(im2, Counter, (YHEIGHT-YOFFSET) - p2p2[Counter], Counter, (YHEIGHT-YOFFSET) - udp2[Counter] - 1, purple2);
                gdImageLine(im2, Counter, (YHEIGHT-YOFFSET) - http2[Counter], Counter, (YHEIGHT-YOFFSET) - p2p2[Counter] - 1, blue2);
                gdImageLine(im2, Counter, (YHEIGHT-YOFFSET) - ftp2[Counter], Counter, (YHEIGHT-YOFFSET) - http2[Counter] - 1, lblue2);


                }
            }

	if (SentPeak < 1024/8)
		snprintf(Buffer2, 50, "Peak Send Rate: %.1f Bits/sec", (double)SentPeak*8);
	else if (SentPeak < (1024*1024)/8)
		snprintf(Buffer2, 50, "Peak Send Rate: %.1f KBits/sec", ((double)SentPeak*8.0)/1024.0);
	else snprintf(Buffer2, 50, "Peak Send Rate: %.1f MBits/sec", ((double)SentPeak*8.0)/(1024.0*1024.0));
								
	if (SummaryData->TotalSent < 1024)
		snprintf(Buffer, 30, "Sent %.1f Bytes", (double)SummaryData->TotalSent);					
	else if (SummaryData->TotalSent < 1024*1024)
		snprintf(Buffer, 30, "Sent %.1f KBytes", (double)SummaryData->TotalSent/1024.0);
	else snprintf(Buffer, 30, "Sent %.1f MBytes", (double)SummaryData->TotalSent/(1024.0*1024.0));

	gdImageString(im, gdFontSmall, XOFFSET+5,  YHEIGHT-20, Buffer, black);
	gdImageString(im, gdFontSmall, XWIDTH/2+XOFFSET/2,  YHEIGHT-20, Buffer2, black);				

	if (ReceivedPeak < 1024/8)
       	snprintf(Buffer2, 50, "Peak Receive Rate: %.1f Bits/sec", (double)ReceivedPeak*8);
    else if (ReceivedPeak < (1024*1024)/8)
    	snprintf(Buffer2, 50, "Peak Receive Rate: %.1f KBits/sec", ((double)ReceivedPeak*8.0)/1024.0);               
	else snprintf(Buffer2, 50, "Peak Receive Rate: %.1f MBits/sec", ((double)ReceivedPeak*8.0)/(1024.0*1024.0));

    if (SummaryData->TotalReceived < 1024)
        snprintf(Buffer, 30, "Received %.1f Bytes", (double)SummaryData->TotalReceived);
    else if (SummaryData->TotalReceived < 1024*1024)
        snprintf(Buffer, 30, "Received %.1f KBytes", (double)SummaryData->TotalReceived/1024.0);
    else snprintf(Buffer, 30, "Received %.1f MBytes", (double)SummaryData->TotalReceived/(1024.0*1024.0));
                                                                                                              
    gdImageString(im2, gdFontSmall, XOFFSET+5,  YHEIGHT-20, Buffer, black2);                
    gdImageString(im2, gdFontSmall, XWIDTH/2+XOFFSET/2,  YHEIGHT-20, Buffer2, black2);

    return(YMax);
    }

void PrepareYAxis(gdImagePtr im, unsigned long long int YMax)
    {
    char buffer[20];

	char YLegend;
	long long int Divisor;

    int black;
    float YTic = 0;
    double y;
    long int YStep;
    
    black = gdImageColorAllocate(im, 0, 0, 0);
    gdImageLine(im, XOFFSET, 0, XOFFSET, YHEIGHT, black);

    YLegend = ' ';
    Divisor = 1;
    if (YMax*8 > 1024*2)
        {
        Divisor = 1024;    // Display in K
        YLegend = 'k';
        }
    if (YMax*8 > 1024*1024*2)
        {
        Divisor = 1024*1024; // Display in M
        YLegend = 'm';
        }
    if (YMax*8 > (long long)1024*1024*1024*2)
        {
        Divisor = 1024*1024*1024; // Display in G
        YLegend = 'g';
        }

    YStep = YMax/10;
    if (YStep < 1)
        YStep=1;
    YTic=YStep;

    while (YTic < (YMax - YMax/10))
        {
        y = (YHEIGHT-YOFFSET)-((YTic*(YHEIGHT-YOFFSET))/YMax);        

        gdImageLine(im, XOFFSET, y, XWIDTH, y, black);        
        snprintf(buffer, 20, "%4.1f %cbits/s", (float)(8.0*YTic)/Divisor, YLegend);
        gdImageString(im, gdFontSmall, 3, y-7, buffer, black);        

        YTic += YStep;
        }
    } 

void PrepareXAxis(gdImagePtr im, time_t timestamp)
    {
    char buffer[100];
    int black, red;
    time_t sample_begin, sample_end;    
    struct tm *timestruct;
    long int MarkTime;
	long int MarkTimeStep;
    double x;
    
    sample_begin=timestamp-config.range;
    sample_end=sample_begin+config.interval;

    black = gdImageColorAllocate(im, 0, 0, 0);
    red   = gdImageColorAllocate(im, 255, 0, 0);

    gdImageLine(im, 0, YHEIGHT-YOFFSET, XWIDTH, YHEIGHT-YOFFSET, black);

    // ********************************************************************
    // ****  Write the red day/month seperator bars
    // ********************************************************************

	if ((24*60*60*(XWIDTH-XOFFSET))/config.range > (XWIDTH-XOFFSET)/10)
		{
		// Day bars
	    timestruct = localtime((time_t *)&sample_begin);
    	timestruct->tm_sec = 0;
	    timestruct->tm_min = 0;
    	timestruct->tm_hour = 0;
	    MarkTime = mktime(timestruct);
            
    	x = (MarkTime-sample_begin)*( ((double)(XWIDTH-XOFFSET)) / config.range) + XOFFSET;
	    while (x < XOFFSET)
    	    {
        	MarkTime += (24*60*60);
	        x = (MarkTime-sample_begin)*((XWIDTH-XOFFSET)/config.range) + XOFFSET;
    	    }

	    while (x < (XWIDTH-10))
    	    {
        	// Day Lines
	        gdImageLine(im, x, 0, x, YHEIGHT-YOFFSET, red);
    	    gdImageLine(im, x+1, 0, x+1, YHEIGHT-YOFFSET, red);
	
    	    timestruct = localtime((time_t *)&MarkTime);
	        strftime(buffer, 100, "%a, %b %d", timestruct);
    	    gdImageString(im, gdFontSmall, x-30,  YHEIGHT-YOFFSET+10, buffer, black);        

	        // Calculate Next x
    	    MarkTime += (24*60*60);
        	x = (MarkTime-sample_begin)*((XWIDTH-XOFFSET)/config.range) + XOFFSET;
	        }
		}
	else
		{
    	// Month Bars
        timestruct = localtime((time_t *)&sample_begin);
        timestruct->tm_sec = 0;
        timestruct->tm_min = 0;
        timestruct->tm_hour = 0;
		timestruct->tm_mday = 1;
		timestruct->tm_mon--; // Start the month before the sample
        MarkTime = mktime(timestruct);

    	x = (MarkTime-sample_begin)*( ((double)(XWIDTH-XOFFSET)) / config.range) + XOFFSET;
	    while (x < XOFFSET)
    	    {
			timestruct->tm_mon++;
        	MarkTime = mktime(timestruct);
	        x = (MarkTime-sample_begin)*((XWIDTH-XOFFSET)/config.range) + XOFFSET;
    	    }

	    while (x < (XWIDTH-10))
    	    {
        	// Month Lines
	        gdImageLine(im, x, 0, x, YHEIGHT-YOFFSET, red);
    	    gdImageLine(im, x+1, 0, x+1, YHEIGHT-YOFFSET, red);
	
    	    timestruct = localtime((time_t *)&MarkTime);
	        strftime(buffer, 100, "%b", timestruct);
    	    gdImageString(im, gdFontSmall, x-6,  YHEIGHT-YOFFSET+10, buffer, black);        

	        // Calculate Next x
            timestruct->tm_mon++;
            MarkTime = mktime(timestruct);
        	x = (MarkTime-sample_begin)*((XWIDTH-XOFFSET)/config.range) + XOFFSET;
	        }				
		}

    // ********************************************************************
    // ****  Write the tic marks
    // ********************************************************************

    timestruct = localtime((time_t *)&sample_begin);
    timestruct->tm_sec = 0;
    timestruct->tm_min = 0;
    timestruct->tm_hour = 0;
    MarkTime = mktime(timestruct);

	if ((6*60*60*(XWIDTH-XOFFSET))/config.range > 10) // pixels per 6 hours is more than 2
		MarkTimeStep = 6*60*60; // Major ticks are 6 hours
	else if ((24*60*60*(XWIDTH-XOFFSET))/config.range > 10)
		MarkTimeStep = 24*60*60; // Major ticks are 24 hours;
	else
		return; // Done		

	x = (MarkTime-sample_begin)*((XWIDTH-XOFFSET)/config.range) + XOFFSET;
	while (x < XOFFSET)
   		{
		MarkTime += MarkTimeStep;
	    x = (MarkTime-sample_begin)*((XWIDTH-XOFFSET)/config.range) + XOFFSET;
    	}

    while (x < (XWIDTH-10))
    	{
	    if (x > XOFFSET) {
    		gdImageLine(im, x, YHEIGHT-YOFFSET-5, x, YHEIGHT-YOFFSET+5, black);
	       	gdImageLine(im, x+1, YHEIGHT-YOFFSET-5, x+1, YHEIGHT-YOFFSET+5, black);
        	}
		MarkTime += MarkTimeStep;
   		x = (MarkTime-sample_begin)*((XWIDTH-XOFFSET)/config.range) + XOFFSET;
        }

    timestruct = localtime((time_t *)&sample_begin);
    timestruct->tm_sec = 0;
    timestruct->tm_min = 0;
    timestruct->tm_hour = 0;
    MarkTime = mktime(timestruct);

	if ((60*60*(XWIDTH-XOFFSET))/config.range > 2) // pixels per hour is more than 2
		MarkTimeStep = 60*60;  // Minor ticks are 1 hour
	else if ((6*60*60*(XWIDTH-XOFFSET))/config.range > 2)
		MarkTimeStep = 6*60*60; // Minor ticks are 6 hours
	else if ((24*60*60*(XWIDTH-XOFFSET))/config.range > 2)
		MarkTimeStep = 24*60*60;
	else
		return; // Done

	// Draw Minor Tic Marks
	x = (MarkTime-sample_begin)*((XWIDTH-XOFFSET)/config.range) + XOFFSET;

	while (x < XOFFSET)
   		{
		MarkTime += MarkTimeStep;
	    x = (MarkTime-sample_begin)*((XWIDTH-XOFFSET)/config.range) + XOFFSET;
    	}

    while (x < (XWIDTH-10))
        {
	    if (x > XOFFSET) {
    		gdImageLine(im, x, YHEIGHT-YOFFSET, x, YHEIGHT-YOFFSET+5, black);
        	gdImageLine(im, x+1, YHEIGHT-YOFFSET, x+1, YHEIGHT-YOFFSET+5, black);
            }
	    MarkTime+=MarkTimeStep;
    	x = (MarkTime-sample_begin)*((XWIDTH-XOFFSET)/config.range) + XOFFSET;
        }
    }


